package com.gw.yunke.utils;


import com.sun.crypto.provider.SunJCE;

import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class EncryptUtils {

    private static final Logger LOG = LoggerFactory.getLogger(EncryptUtils.class);
    private static final String ENC = "utf-8";

    public static String sha256HMACEncode(String params, String secret) {
        String result = "";
        try {
            Mac sha256HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            //sha256HMAC.init(secretKey);
            //byte[] sha256HMACBytes = sha256HMAC.doFinal(params.getBytes());

            String hash = Base64.encodeBase64String(params.getBytes("utf-8"));
            if (LOG.isDebugEnabled()) {
                LOG.debug("sha256HMAC encode = " + hash);
            }
            return hash;
        } catch (Exception e) {
            LOG.error("sha256HMACEncode failed.", e);
        }
        return result;
    }


    public static String generateSignature(String params, String appSecret) {
        String result = md5(sha256HMACEncode(params, appSecret)).substring(5, 15);
        if (LOG.isDebugEnabled()) {
            LOG.debug("generateSignature result = " + result);
        }
       

        return result;
    }

    public static String md5s(String plainText) {
        String str = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(plainText.getBytes());
            byte[] b = md.digest();

            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                int i = b[offset];
                if (i < 0) {
                    i += 256;
                }
                if (i < 16) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(i));
            }
            str = buf.toString();

            str = buf.toString();
        } catch (NoSuchAlgorithmException e) {
        }

        return str;
    }


    public static String md5(String value) {
        try {
            MessageDigest md = MessageDigest.getInstance("md5");
            byte[] e = md.digest(value.getBytes());
            return byteToHexString(e);
        } catch (NoSuchAlgorithmException e) {
        }
        return null;
    }

    public static String byteToHexString(byte[] salt) {
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < salt.length; i++) {
            String hex = Integer.toHexString(salt[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            hexString.append(hex.toLowerCase());
        }
        return hexString.toString();
    }

    public static String md5new(String pwd) {
        try {
            // 创建加密对象
            MessageDigest digest = MessageDigest.getInstance("md5");

            // 调用加密对象的方法，加密的动作已经完成
            byte[] bs = digest.digest(pwd.getBytes());
            // 接下来，我们要对加密后的结果，进行优化，按照mysql的优化思路走
            // mysql的优化思路：
            // 第一步，将数据全部转换成正数：
            String hexString = "";
            for (byte b : bs) {
                // 第一步，将数据全部转换成正数：
                // 解释：为什么采用b&255
                /*
                 * b:它本来是一个byte类型的数据(1个字节) 255：是一个int类型的数据(4个字节)
                 * byte类型的数据与int类型的数据进行运算，会自动类型提升为int类型 eg: b: 1001 1100(原始数据)
                 * 运算时： b: 0000 0000 0000 0000 0000 0000 1001 1100 255: 0000
                 * 0000 0000 0000 0000 0000 1111 1111 结果：0000 0000 0000 0000
                 * 0000 0000 1001 1100 此时的temp是一个int类型的整数
                 */
                int temp = b & 255;
                // 第二步，将所有的数据转换成16进制的形式
                // 注意：转换的时候注意if正数>=0&&<16，那么如果使用Integer.toHexString()，可能会造成缺少位数
                // 因此，需要对temp进行判断
                if (temp < 16 && temp >= 0) {
                    // 手动补上一个“0”
                    hexString = hexString + "0" + Integer.toHexString(temp);
                } else {
                    hexString = hexString + Integer.toHexString(temp);
                }
            }
            LOG.info("md5 = {}", hexString);
            return hexString;
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "";
    }

    @Deprecated
    public static String mapToFormatString(Map<String, Object> map) {
        StringBuilder sb = new StringBuilder();
        String result = "";
        if (map.size() > 0) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                sb.append(entry.getKey());
                sb.append("=");
                sb.append(entry.getValue());
                sb.append("&");
            }
            result = sb.replace(sb.length() - 1, sb.length(), "").toString();
        }
        try {
            //处理参数中有URL的情况

            result = URLEncoder.encode(result, ENC)
                    .replace("%3D", "=")
                    .replace("%26", "&");

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            LOG.warn("Encrept Encode Error,UnsupportedEncodingException e = {}", e);
            return "";
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("mapToFormatString result = " + result);
        }
        return result;
    }


    /**
     * Original method will treat all '=' as connector
     * this one won't convert '=' in value by mistake
     *
     * @param map
     * @param fixed
     * @return
     */
    public static String mapToFormatString(Map<String, Object> map, boolean fixed) {
        String result = "";
        if (!fixed) {
            result = mapToFormatString(map);
        } else {
            List<String> ss = new ArrayList<>();

            try {
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    String key = entry.getKey();
                    String encodedValue = URLEncoder.encode(String.valueOf(entry.getValue()), ENC);
                    // replace with encoded value
                    ss.add(key + "=" + encodedValue);
                    System.out.println(String.format("Key: %s Value: %s", key, encodedValue));
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            result = String.join("&", ss);
        }
        return result;
    }

    /**
     * 签名计算
     *
     * @param
     * @return Treemap 带签名参数值
     **/
    @Deprecated
    public static TreeMap<String, Object> signature(String appkey, String appsecret, TreeMap<String, Object> map) {
        long expires = System.currentTimeMillis() / 1000 + 60;
        //String nonce = String.valueOf(RandomUtils.nextLong(1L, 999999L));
        map.put("appkey", appkey);
        map.put("expires", expires);
        //map.put("nonce", nonce);
        String signature = EncryptUtils.generateSignature(EncryptUtils.mapToFormatString(map), appsecret);
        map.put("signature", signature);
        return map;
    }

    /**
     * use this!
     *
     * @param appkey
     * @param appsecret
     * @param map
     * @param customzied
     * @return
     */
    public static TreeMap<String, Object> signature(String appkey, String appsecret, TreeMap<String, Object> map,
                                                    boolean customzied) {
        if (!customzied) {
            return signature(appkey, appsecret, map);
        } else {
            long expires = System.currentTimeMillis() / 1000 + 60;
            //String nonce = String.valueOf(RandomUtils.nextLong(1L, 999999L));
            map.put("appkey", appkey);
            map.put("expires", expires);
            //map.put("expires", "1513680790");
            //map.put("nonce", nonce);
            //map.put("nonce", "5a38ef3211f6b");
            String signature = EncryptUtils.generateSignature(EncryptUtils.mapToFormatString(map, true), appsecret);
            map.put("signature", signature);
            return map;
        }
    }

    public static String sha256HMACDecode(String params, String secret) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        String result = "";
        try {
            Mac sha256HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            //sha256HMAC.init(secretKey);
            //byte[] sha256HMACBytes = sha256HMAC.doFinal(params.getBytes("utf-8"));
            byte[] decode = Base64.decodeBase64(params);
            if (LOG.isDebugEnabled()) {
                LOG.debug("sha256HMAC decode = " + decode);
            }

            for(byte b : decode){
                System.out.print(b+"  ");
            }

            return new String(decode,"utf-8");
        } catch (Exception e) {
            LOG.error("sha256HMACEncode failed.", e);
            throw e;
        }
        //return result;
    }

    public String desEncrypt(){
        Security.addProvider(new SunJCE());
        byte[] arr = {1,3,5,2,4,6,7,8};
        //Key key = new SecretKeySpec()
        return null;
    }

    public String getDesKey(){

        return null;
    }

    public static String byteArr2HexString(byte arr[]){
        StringBuffer stringBuffer = new StringBuffer(arr.length*2);
        for(int i = 0;i < arr.length;i++){
            int tmp = arr[i];
            while(tmp < 0){
                tmp = tmp + 256;
            }
            if(tmp < 16){
                stringBuffer.append("0");
            }
            stringBuffer.append(Integer.toString(tmp,16));
        }
        return stringBuffer.toString();
    }

    public static void main(String args[]) throws UnsupportedEncodingException, NoSuchAlgorithmException {

        String s = "userName=jack,email=12345@h.com,id=1";
        String encrypt = sha256HMACEncode(s,"yunke");
        String result = sha256HMACDecode(encrypt,"yunke");

        System.out.println(encrypt);
        System.out.println(result);


    }
}
